﻿using LPBToolsLib;
using StationSer;
using System;
using System.Collections.Generic;
using System.Threading;

namespace StationSer
{
    public interface IFStation
    {
        DateTime LastActiveTime { get; }
        /// <summary>
        /// PLC工作站类型ID
        /// </summary>
        int PLCStationType { get; }
        /// <summary>
        /// 工作站ID
        /// </summary>
        string StationID { get; }
        /// <summary>
        /// 是否获得新的寄存器数据，读取后变成false
        /// </summary>
        bool hasFlash { get; }
        /// <summary>
        /// PLC寄存器起始地址
        /// </summary>
        int PLCRegStar { get; }
        /// <summary>
        /// PLC寄存器数量
        /// </summary>
        int PLCRegCount { get; }
        /// <summary>
        /// PLC所有寄存器镜像值
        /// </summary>
        ushort[] RegValues { get; }
        //void reFlashRegValues(int iRegStar, ushort[] tNewRegValues);
        /// <summary>
        /// 收到Option的处理接口
        /// </summary>
        /// <param name="sender">通讯控件对象</param>
        /// <param name="OptionDatas">收到的Option数据</param>
        /// <param name="ResponOpt">返回应答数据</param>
        void RecOption(IFDExCommunicater sender, byte[] OptionDatas, out byte[] ResponOpt);
        /// <summary>
        /// 设置向工作站应答或下达的命令
        /// </summary>
        /// <param name="tOption"></param>
        void setResponCommand(DExOptionBase tOption);
    }

    public abstract class StationBase :ContainerBase
    {
        public virtual int PLCStationType { get; } = -1;
        public Recorder_Error SysErr { get; set; } = null;
        public Recorder_RunLog RunLog { get; set; } = null;
        /// <summary>
        /// 最后活动时间
        /// </summary>
        public DateTime LastActiveTime { get; protected set; } = DateTime.Now;
        /// <summary>
        /// 能够持续的时间长度
        /// </summary>
        public int Interval_ms { get; protected set; } = 5000;
        /// <summary>
        /// 对象是否已经超时
        /// </summary>
        public bool IsTimeOut
        {
            get
            {
                return ((DateTime.Now - LastActiveTime).TotalMilliseconds > Interval_ms);
            }
        }

        private static string MutexKeyStr = "StationBase" + Guid.NewGuid().ToString();
        private Mutex tMutex { get; } = new Mutex(false, MutexKeyStr);
        public virtual int PLCRegStar { get; protected set; } = 0;

        public int PLCRegCount
        {
            get { return StationRegMirror.Count; }
            protected set
            {
                if (value >= 0)
                {
                    tMutex.WaitOne();
                    try
                    {
                        StationRegMirror.Clear();
                        if (value > 0)
                            StationRegMirror.AddRange(new ushort[value]);
                    }
                    finally
                    {
                        tMutex.ReleaseMutex();
                    }
                }
            }
        }
        protected List<ushort> StationRegMirror { get; } = new List<ushort>();
        public ushort[] RegValues { get { return StationRegMirror.ToArray(); } }
        public string StationID { get; protected set; } = "";
        private bool iFlash = false;
        public bool hasFlash
        {
            get
            {
                bool Tmp = iFlash;
                iFlash = false;
                return Tmp;
            }
        } 
        /// <summary>
        /// 用新收到的上传数据，更新寄存器镜像中的数据
        /// </summary>
        /// <param name="iRegStar">更新的起始地址，使用全寄存器绝对地址，从0开始</param>
        /// <param name="tNewRegValues"></param>
        protected void reFlashRegValues(int iRegStar, ushort[] tNewRegValues)
        {
            LastActiveTime = DateTime.Now;
            if ((tNewRegValues != null) && (iRegStar >= PLCRegStar)
                && (iRegStar < PLCRegCount + PLCRegStar)
                && (iRegStar + tNewRegValues.Length <= PLCRegCount + PLCRegStar))
            {
                tMutex.WaitOne();
                try
                {
                    for (int i = 0; i < tNewRegValues.Length; i++)
                        StationRegMirror[iRegStar + i - PLCRegStar] = tNewRegValues[i];
                }
                finally
                {
                    tMutex.ReleaseMutex();
                }
                iFlash = true;
            }
        }

        /// <summary>
        /// 提取寄存器镜像中指定地址的值，使用绝对地址，从0开始；
        /// </summary>
        /// <param name="iRegAddress"></param>
        /// <param name="RegValue">成功则返回寄存器的值</param>
        /// <returns>地址正确返回True</returns>
        protected bool getRegValue(int iRegAddress,out ushort RegValue)
        {
            RegValue = 0;
            if ((iRegAddress>=PLCRegStar)&&(iRegAddress< PLCRegStar+PLCRegCount))
            {
                RegValue = StationRegMirror[iRegAddress - PLCRegStar];
                return true;
            }
            return false;
        }

        public StationBase(string stationID)
        {
            PLCRegCount = 0;
            StationID = stationID;
        }

    }

    /// <summary>
    /// PLC工作站基础类型对象
    /// 仅作为初始解析命令使用
    /// </summary>
    public class PLCStation : StationBase, IFStation
    {
        /// <summary>
        /// PLC工作站类型ID，存放数据库中PLC类型ID，从1开始实际PLC类型
        /// </summary>
        public override int PLCStationType { get; } = 0;

        /// <summary>
        /// 创建空的工作站对象，仅用于测试
        /// </summary>
        /// <param name="stationID"></param>
        /// <param name="DestRegCount"></param>
        /// <param name="DestRegStar"></param>
        public PLCStation(string stationID, int DestRegCount,int DestRegStar=0) : base(stationID)
        {
            PLCRegCount = DestRegCount;
            PLCRegStar = DestRegStar;
        }
        /// <summary>
        /// 根据收到的Option创建PLC工作站
        /// 注意：该方式创建的PLC镜像中，寄存器起始地址就是Option的起始地址
        /// </summary>
        /// <param name="stationID"></param>
        /// <param name="UploadRegValue"></param>
        public PLCStation(string stationID, DExOptionBase UploadRegValue) : base(stationID)
        {
            if (UploadRegValue != null)
            {
                RunLog = UploadRegValue.RunLog;
                SysErr = UploadRegValue.SysErr;
            }
            if ((UploadRegValue != null)
                && (UploadRegValue.OptCommand == DExOpt.Opt_UploadRegValue_0x03))
            {
                DExOption_UploadRegValue_0x03 tUploadRegValue = DExOption_UploadRegValue_0x03.createOption(UploadRegValue);
                if ((tUploadRegValue.UploadReadRegCount > 0)
                && (tUploadRegValue.UploadReadRegStarAddr >= 0))
                {
                    PLCRegCount = tUploadRegValue.UploadReadRegCount;
                    PLCRegStar = tUploadRegValue.UploadReadRegStarAddr;
                    reFlashRegValues(tUploadRegValue);
                }
            }
        }       

        protected void reFlashRegValues(DExOptionBase tDOption)
        {
            LastActiveTime = DateTime.Now;
            if (tDOption != null)
            {
                if (tDOption.OptCommand == DExOpt.Opt_UploadRegValue_0x03)
                {
                    DExOption_UploadRegValue_0x03 tOpt03 = DExOption_UploadRegValue_0x03.createOption(tDOption);
                    if ((tOpt03 != null)
                        && (tOpt03.UploadReadRegCount > 0)
                        && (tOpt03.UploadReadRegCount == tOpt03.UploadReadRegValues.Length)
                        && (tOpt03.UploadReadRegStarAddr == PLCRegStar))
                    {
                        if (tOpt03.UploadReadRegCount != PLCRegCount)
                            PLCRegCount = tOpt03.UploadReadRegCount;
                        ushort[] tNewRegValues = tOpt03.UploadReadRegValues;
                        reFlashRegValues(tOpt03.UploadReadRegStarAddr, tNewRegValues);
                    }
                }
            }
        }

        /// <summary>
        /// PLC工作站接受下位机上传的命令接口，通过通讯管道的setStation函数设置到OnRecOptioin事件中。
        /// 注意：该函数是在通讯管道的线程中执行
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="OptionDatas"></param>
        /// <param name="ResponOpt"></param>
        public virtual void RecOption(IFDExCommunicater sender, byte[] OptionDatas, out byte[] ResponOpt)
        {
            ResponOpt = null;
            if ((OptionDatas != null) && (OptionDatas.Length >= 4))
            {
                DExOptionBase tDOption = new DExOptionBase(OptionDatas);
                while ((tDOption != null) && tDOption.OptReady)
                {
                    reFlashRegValues(tDOption);
                    tDOption = tDOption.OptionOfNext();
                }
            }
            RunLog?.setRecord(new Record(toDetail()));
            /*
             ? 根据数据库中StationID对应的PLC类型创建对象
                使用sender.setStation函数将新的PLC类型对象设置为通讯管道输出对象
             
             */
            // 在应答中追加需要下达的命令数据
            if (tResponOption != null)
            {
                ResponOpt = tResponOption.OptionDatas;
                tResponOption = null;
            }
            LastActiveTime = DateTime.Now;
        }
        private DExOptionBase tResponOption { get; set; } = null;
        public void setResponCommand(DExOptionBase tOption)
        {
            tResponOption = tOption;
        }
    }


    
}